package it.multimodale.twitterrecognizer.speechutility;

import it.interazionemultimodale.twitterrecognizer.twitterutility.TweetInfo;
import it.interazionemultimodale.twitterrecognizer.twitterutility.TwitterManager;
import it.interazionemultimodale.twitterrecognizer.ui.HomeActivity;
import it.interazionemultimodale.twitterrecognizer.ui.TweetAdapter;
import it.interazionemultimodale.twitterrecognizer.utility.Methods;
import it.interazionemultimodale.twitterrecognizer.utility.Variables;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import com.markupartist.android.widget.PullToRefreshListView;

import android.os.Bundle;
import android.speech.RecognitionListener;
import android.speech.SpeechRecognizer;
import android.speech.tts.TextToSpeech;
import android.speech.tts.UtteranceProgressListener;

public class SpeechListener extends UtteranceProgressListener implements RecognitionListener, Runnable
{
	//Speech command constants
	private static final int POST_COMMAND_LENGTH = 2;
	private static final int CONFIRM_OR_CANCEL_COMMAND_LENGTH = 1;
	private static final int LOAD_AND_READ_COMMAND_LENGTH = 3;
	private static final int TWEET_NUMBER_COMMAND_LENGTH = 1;
	private static final ArrayList<String> TWEET_COMMAND = new ArrayList<String>(Arrays.asList("tweet", "twitter"));
		/*
		 * Possible commands to post a tweet; in italian: "nuovo tweet", "scrivi tweet", "posta tweet",
		 * "nuovo twitter", "scrivi twitter", "posta twitter" 
		 */
	private static final ArrayList<String> POST_COMMAND = new ArrayList<String>(Arrays.asList("nuovo", "scrivi", "posta", "pubblica"));
		//Possible commands to confirm or cancel to post a tweet
	private static final ArrayList<String> CONFIRM_COMMAND = new ArrayList<String>(Arrays.asList("si", "se", "conferma", "procedi", "posta", "pubblica", "vai"));
	private static final ArrayList<String> CANCEL_COMMAND = new ArrayList<String>(Arrays.asList("annulla", "blocca", "cancella", "no", "stop"));
		//Possible commands to read twitter statuses
	private static final ArrayList<String> READ_COMMAND = new ArrayList<String>(Arrays.asList("leggi", "leggimi", "dimmi"));
		//Command to load old and new tweets into the list view
	private static final String LOAD_COMMAND = "carica";
	private static final String OLD_COMMAND = "vecchi";
	private static final String NEW_COMMAND = "nuovi";
	
	//RecognitionListener commands
	private static enum ListenerCommand { COMMAND_MANAGER, REQUEST_TWEET_TO_POST, CONFIRM_TWEET_TO_POST , REQUEST_NUMBER_TWEETS };
	private static String RECOGNITION_LISTENER_CALLER_KEY = "recognition_command";
	private static String CALLER_COMMAND_READ = "read";
	private static String CALLER_COMMAND_LOAD = "load";
	
	//UtteranceProgressListenerCommands
	public static final String ON_UTTERANCE_REQUEST_TWEET = "request";
	public static final String ON_UTTERANCE_CONFIRM_TWEET = "confirm";
	public static final String ON_UTTERANCE_CONFIRMED_TWEET = "confirmed";
	public static final String ON_UTTERANCE_REQUEST_NUMBER = "number";
	public static final String ON_UTTERANCE_READ_TWEET_MANAGEMENT = "tweet";
	public static final String ON_UTTERANCE_STOP = "stop";
	
	//Array of speech commands recognized by RecognitionListener
	private ArrayList<String> stringsRecognized;
	
	/*
	 * Caller activity. Used to return to execute speech recognition on the main thread, 
	 * and to manage gui
	 */
	private HomeActivity callerActivity;
	
	//Used to refresh the list view that contains tweets 
	private PullToRefreshListView listView;
	private TweetAdapter adapter;
	
	/*
	 * RecognitionListener management commands. Note: this is a trick to reuse the same listener.
	 */
	private ListenerCommand listenerCommand = ListenerCommand.COMMAND_MANAGER;
	
	//The text recognized used only to post as tweet
	private String textToPost;
	
	public SpeechListener(HomeActivity callerActivity, PullToRefreshListView listView, TweetAdapter adapter)
	{
		this.callerActivity = callerActivity;
		this.listView = listView;
		this.adapter = adapter;
	}
	
	//Start of override methods of Runnable class
	@Override
	public void run()
	{		
		String[] commandWords;
		boolean commandUnderstood = false;
		switch (this.listenerCommand)
		{
			case REQUEST_NUMBER_TWEETS:
				commandWords = this.stringsRecognized.get(0).split(" ");
				if (commandWords.length == TWEET_NUMBER_COMMAND_LENGTH)
				{
					try
					{
						int tweetNumber = Integer.parseInt(commandWords[0]);

						if (tweetNumber > 0)
						{
							this.callerActivity.dismissSpeechDialog();
							String callerCommand = Variables.ttsListenersMapAsk.get(RECOGNITION_LISTENER_CALLER_KEY);
							if (callerCommand.compareTo(ListenerCommand.REQUEST_NUMBER_TWEETS + CALLER_COMMAND_READ) == 0)
							{
								this.callerActivity.setTweetProgressBarMessage("Caricamento...");
								this.callerActivity.showTweetProgressDialog();
								this.getAndReadAloudTweets(tweetNumber);
							}
							else if (callerCommand.compareTo(ListenerCommand.REQUEST_NUMBER_TWEETS + CALLER_COMMAND_LOAD) == 0)
							{
								Variables.OLDER_TWEETS_TO_VISUALIZE = tweetNumber;
								if (tweetNumber == 1)
									Variables.textToSpeech.speak("Carico un vecchio tuit. Attendere.", TextToSpeech.QUEUE_ADD, null);
								else
									Variables.textToSpeech.speak("Carico " + tweetNumber + " vecchi tuit. Attendere.", TextToSpeech.QUEUE_ADD, null);
								
								this.callerActivity.downloadAndVisualizeTweets(Variables.TwitterTaskCommand.OLDER_TWEETS);
							}
						}
						else
						{
							this.callerActivity.enableButtons();
							this.callerActivity.dismissSpeechDialog();
							Variables.textToSpeech.speak("Operazione annullata", TextToSpeech.QUEUE_ADD, null);
						}
					}
					catch (NumberFormatException nfe)
					{
						Variables.textToSpeech.speak("Spiacente. Non ho capito il numero", TextToSpeech.QUEUE_ADD, null);
						this.callerActivity.enableButtons();
						this.callerActivity.dismissSpeechDialog();
					}
					finally
					{
						this.listenerCommand = ListenerCommand.COMMAND_MANAGER;
					}
				}
				break;
			case CONFIRM_TWEET_TO_POST:
				for (String command : this.stringsRecognized)
				{
					commandWords = command.split(" ");
					if (commandWords.length == CONFIRM_OR_CANCEL_COMMAND_LENGTH)
					{
						if (Methods.stringInArray(commandWords[0], SpeechListener.CONFIRM_COMMAND))
						{
							commandUnderstood = true;
							this.callerActivity.dismissSpeechDialog();
							Variables.ttsListenersMapAsk.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, ON_UTTERANCE_CONFIRMED_TWEET);
							Variables.textToSpeech.speak("Confermato. Procedo alla pubblicazione del tuit. Attendere.", TextToSpeech.QUEUE_ADD, Variables.ttsListenersMapAsk);
						}
						else if (Methods.stringInArray(commandWords[0], SpeechListener.CANCEL_COMMAND))
						{
							commandUnderstood = true;
							Variables.textToSpeech.speak("Operazione annullata", TextToSpeech.QUEUE_ADD, null);
							this.callerActivity.enableButtons();
							this.callerActivity.dismissSpeechDialog();
							this.listenerCommand = ListenerCommand.COMMAND_MANAGER;
						}
						
						if (commandUnderstood)
							break;
					}
				}
				
				if (!commandUnderstood)
				{
					Variables.textToSpeech.speak("Comando non riconosciuto. Operazione annullata", TextToSpeech.QUEUE_ADD, null);
					this.callerActivity.enableButtons();
					this.callerActivity.dismissSpeechDialog();
					this.listenerCommand = ListenerCommand.COMMAND_MANAGER;
				}
				
				break;
			case REQUEST_TWEET_TO_POST:
				this.textToPost = this.stringsRecognized.get(0);
				
				//Manage hashtag
				this.textToPost = this.textToPost.replaceAll("hashtag", "tag");
				
				this.callerActivity.dismissSpeechDialog();
				Variables.ttsListenersMapAsk.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, ON_UTTERANCE_CONFIRM_TWEET);
				Variables.textToSpeech.speak("Ti rileggo il testo. " + this.textToPost + ". Confermare?", TextToSpeech.QUEUE_ADD, Variables.ttsListenersMapAsk);
				break;
			case COMMAND_MANAGER:
				for (String command : this.stringsRecognized)
				{
					commandWords = command.split(" ");
					
					if (commandWords.length == POST_COMMAND_LENGTH)
					{	
						//Here starts the management of command to post a new tweet
						if (Methods.stringInArray(commandWords[0], SpeechListener.POST_COMMAND) && Methods.stringInArray(commandWords[1], SpeechListener.TWEET_COMMAND))
						{
							commandUnderstood = true;
							this.callerActivity.dismissSpeechDialog();
							Variables.ttsListenersMapAsk.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, ON_UTTERANCE_REQUEST_TWEET);
							Variables.textToSpeech.speak("Dimmi il tuit", TextToSpeech.QUEUE_ADD, Variables.ttsListenersMapAsk);
						}
					}
					else if (commandWords.length == LOAD_AND_READ_COMMAND_LENGTH)
					{	
						//Here starts the management of command to read tweets aloud
						if (Methods.stringInArray(commandWords[0], SpeechListener.READ_COMMAND) && Methods.stringInArray(commandWords[2], SpeechListener.TWEET_COMMAND))
						{
							try
							{
								int tweetNumber = 0;
								if (commandWords[1].compareToIgnoreCase("un") == 0)
									tweetNumber = 1;
								if (tweetNumber == 0)
									tweetNumber = Integer.parseInt(commandWords[1]);
								
								if (tweetNumber > 0)
								{
									this.callerActivity.dismissSpeechDialog();
									this.callerActivity.setTweetProgressBarMessage("Caricamento...");
									this.callerActivity.showTweetProgressDialog();
									this.getAndReadAloudTweets(tweetNumber);
								}
								
								this.listenerCommand = ListenerCommand.COMMAND_MANAGER;
							}
							catch (NumberFormatException nfe)
							{
								this.callerActivity.dismissSpeechDialog();
								Variables.ttsListenersMapAsk.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, ON_UTTERANCE_REQUEST_NUMBER);
								Variables.ttsListenersMapAsk.put(RECOGNITION_LISTENER_CALLER_KEY, ListenerCommand.REQUEST_NUMBER_TWEETS + CALLER_COMMAND_READ);
								Variables.textToSpeech.speak("Non ho capito quanti tuit vuoi leggere. Potresti dirmelo?", TextToSpeech.QUEUE_ADD, Variables.ttsListenersMapAsk);
							}
							finally
							{
								commandUnderstood = true;
							}
						}
						else if (commandWords[0].compareToIgnoreCase(LOAD_COMMAND) == 0 &&
									Methods.stringInArray(commandWords[2], SpeechListener.TWEET_COMMAND)) { //Here starts the management of command to load old or new tweets
							
							if (commandWords[1].compareToIgnoreCase(NEW_COMMAND) == 0)
							{
								commandUnderstood = true;
								Variables.textToSpeech.speak("Carico nuovi tuit. Attendere.", TextToSpeech.QUEUE_ADD, null);
								this.callerActivity.dismissSpeechDialog();
								this.callerActivity.downloadAndVisualizeTweets(Variables.TwitterTaskCommand.NEW_TWEETS);
								this.listenerCommand = ListenerCommand.COMMAND_MANAGER;		//TODO: Rivedere
							}
							else if (commandWords[1].compareToIgnoreCase(OLD_COMMAND) == 0)
							{
								commandUnderstood = true;
								this.callerActivity.dismissSpeechDialog();
								Variables.ttsListenersMapAsk.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, ON_UTTERANCE_REQUEST_NUMBER);
								Variables.ttsListenersMapAsk.put(RECOGNITION_LISTENER_CALLER_KEY, ListenerCommand.REQUEST_NUMBER_TWEETS + CALLER_COMMAND_LOAD);
								Variables.textToSpeech.speak("Quanti tuit vuoi caricare?", TextToSpeech.QUEUE_ADD, Variables.ttsListenersMapAsk);
							}
						}
					}
					
					if (commandUnderstood)
						break;
				}
				
				if (!commandUnderstood)
				{
					Variables.textToSpeech.speak("Comando non valido. Prova di nuovo.", TextToSpeech.QUEUE_ADD, null);
					this.callerActivity.enableButtons();
					this.callerActivity.dismissSpeechDialog();
				}
				break;
		}
	}
	//End of override methods of Runnable class
	
	//Start of override methods of RecognitionListener class
	@Override
    public void onResults(Bundle results)
	{
		this.stringsRecognized = results.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION);
		new Thread(this).start();
    }

    @Override
    public void onReadyForSpeech(Bundle params)
    {
		this.callerActivity.showSpeechDialog();
    }

    @Override
    public void onError(int error)
    {
    	//if (error == SpeechRecognizer.ERROR_SPEECH_TIMEOUT)
	    	//Variables.textToSpeech.speak("Ascolto terminato.", TextToSpeech.QUEUE_ADD, null);
    	if (error == SpeechRecognizer.ERROR_NETWORK || error == SpeechRecognizer.ERROR_NETWORK_TIMEOUT)
    		Variables.textToSpeech.speak("Errore di rete. Verificare che la connessione sia attiva e ci sia segnale.", TextToSpeech.QUEUE_ADD, null);
    	
    	this.callerActivity.dismissSpeechDialog();
    	this.callerActivity.enableButtons();
    	this.listenerCommand = ListenerCommand.COMMAND_MANAGER;
    }

    @Override
    public void onBeginningOfSpeech() {}

    @Override
    public void onBufferReceived(byte[] buffer) {}

    @Override
    public void onEndOfSpeech() {}

    @Override
    public void onEvent(int eventType, Bundle params) {}
    
    @Override
    public void onPartialResults(Bundle partialResults) {}

    @Override
    public void onRmsChanged(float rmsdB) {}
    //End of override methods of RecognitionListener class
    
    //Start of override methods of UtteranceProgressListener
    @Override
	public void onDone(String utteranceId)
	{
    	if (utteranceId.startsWith(ON_UTTERANCE_READ_TWEET_MANAGEMENT))
    	{
    		//System.out.println(utteranceId);
    		String[] tweetNumbers = utteranceId.split("/");
    		int currentTweet, totTweets;
    		try
    		{
    			currentTweet = Integer.parseInt(tweetNumbers[1]);
    			totTweets = Integer.parseInt(tweetNumbers[2]);
    			
    			//System.out.println(currentTweet + " di " + totTweets);
    			
    			if (currentTweet <= totTweets)
    				this.callerActivity.setTweetProgressBarMessage("Lettura del tweet " + currentTweet + " di " + totTweets + "...");
    			else
    			{
    				this.callerActivity.dismissTweetProgressDialog();
    				this.callerActivity.enableButtons();
    			}
    		}
    		catch (NumberFormatException ne)
    		{
    			this.callerActivity.dismissTweetProgressDialog();
    		}
    	}
    	else if (utteranceId.compareTo(ON_UTTERANCE_CONFIRMED_TWEET) == 0)
		{
    		//Manage hashtag
    		this.textToPost = this.textToPost.replaceAll("tag ", "#");
    		
    		this.callerActivity.enableButtons();
			this.callerActivity.dismissSpeechDialog();
			this.callerActivity.runOnUiThread(new Runnable() {
				@Override
				public void run() {
					TwitterManager.postTweet(textToPost, callerActivity);
				}
			});
			this.listenerCommand = ListenerCommand.COMMAND_MANAGER;
		}
		else
		{
			if (utteranceId.compareTo(ON_UTTERANCE_REQUEST_TWEET) == 0)
				this.listenerCommand = ListenerCommand.REQUEST_TWEET_TO_POST;
			else if (utteranceId.compareTo(ON_UTTERANCE_CONFIRM_TWEET) == 0)
				this.listenerCommand = ListenerCommand.CONFIRM_TWEET_TO_POST;
			else if (utteranceId.compareTo(ON_UTTERANCE_REQUEST_NUMBER) == 0)
				this.listenerCommand = ListenerCommand.REQUEST_NUMBER_TWEETS;
			
			this.callerActivity.runOnUiThread(new Runnable()
			{
				@Override
				public void run()
				{
					Variables.speechRecognizer.startListening(Methods.getSpeechRecognizerIntent());
				}
			});
		}
	}

	@Override
	public void onError(String utteranceId)
	{
		if (utteranceId.compareTo(ON_UTTERANCE_STOP) == 0)
		{
			this.listenerCommand = ListenerCommand.COMMAND_MANAGER;
			Variables.ttsListenersMapAsk.clear();
		}
	}

	@Override
	public void onStart(String utteranceId) {}
    //End of override methods of UtteranceProgressListener
	
	private void getAndReadAloudTweets(int tweetNumber) throws NumberFormatException
	{
		List<TweetInfo> readTweetList = ((HomeActivity)this.callerActivity).getTweets(tweetNumber);
		if (tweetNumber > readTweetList.size())
			Variables.textToSpeech.speak(readTweetList.size() + " tuit presenti in memoria", TextToSpeech.QUEUE_ADD, null);
		Methods.readTweetsAloud(Variables.textToSpeech, Variables.ttsListenersMapAsk, readTweetList);
	}
}
